<script setup lang="ts">
import type { UploadChangeParam } from 'ant-design-vue';

import type { FileList, UploadProps } from './types';

import { computed, ref, watch } from 'vue';

import { useAccessStore } from '@vben/stores';

import { message } from 'ant-design-vue';

import { spliceSepPrefix, spliceUrlPrefix } from '#/utils';

const props = withDefaults(defineProps<UploadProps>(), {
  name: 'file',
  action: '/api/sys/upload',
  maxCount: 3,
  multiple: false,
  showFileList: true,
  separator: '|',
  maxSize: 5,
  listType: 'picture-card',
  headers: () => ({}),
});
const emit = defineEmits(['update:modelValue']);
const accessStore = useAccessStore();
const token = accessStore.accessToken;

// 创建合并后的 headers 计算属性
const mergedHeaders = computed(() => ({
  ...props.headers,
  Authorization: token,
}));

const fileList = ref<FileList>([]); // 上传图片列表
// 解析初始值
watch(
  () => props.modelValue,
  (val) => {
    if (val) {
      const urls = spliceSepPrefix(val, props?.separator);
      fileList.value = urls.map((url) => ({
        uid: url,
        name: url.slice(Math.max(0, url.lastIndexOf('/') + 1)),
        status: 'done',
        url,
        thumbUrl: /\.(?:jpeg|jpg|gif|png)$/.test(url) ? url : undefined,
      }));
    }
  },
  { immediate: true },
);

// 上传前校验
const beforeUpload = (file: File) => {
  const isValidType = props.accept
    ? props.accept.split(props.separator).includes(file.type)
    : true;
  const isValidSize = file.size / 1024 / 1024 < props.maxSize;

  if (!isValidType) {
    message.error(`仅支持上传 ${props.accept} 格式文件!`);
    return false;
  }

  if (!isValidSize) {
    message.error(`文件大小不能超过 ${props.maxSize}MB!`);
    return false;
  }

  return true;
};

// 处理文件变化
const handleChange = (info: UploadChangeParam) => {
  if (info.file.status === 'done') {
    message.success(`${info.file.name} 上传成功`);
  } else if (info.file.status === 'error') {
    message.error(`${info.file.name} 上传失败`);
  }
  // 生成路径字符串
  const paths = info.fileList
    .filter((file) => file.status === 'done')
    .map((file) => {
      const url = file.response?.result || file.url;
      if (!url) return '';
      return spliceUrlPrefix(url);
    });
  const urlReplace = paths.map((item) => {
    return item.replace(/^.*(?=\/files)/, '');
  });
  //
  emit('update:modelValue', urlReplace.join(props.separator));
};

// 暴露方法给父组件
defineExpose({
  clearFiles: () => (fileList.value = []),
});
</script>

<template>
  <div>
    <a-upload
      v-model:file-list="fileList"
      :action="props.action"
      :multiple="props.multiple"
      :headers="mergedHeaders"
      :max-count="props.maxCount"
      :accept="props.accept"
      :before-upload="beforeUpload"
      @change="handleChange"
      :show-upload-list="props.showFileList"
      :list-type="props.listType"
    >
      <slot>
        <div style="margin-top: 8px" v-if="props.listType === 'picture-card'">
          上传
        </div>
        <a-button v-else type="primary">上传文件</a-button>
      </slot>
    </a-upload>
  </div>
</template>
